package com.jdk.demo.nio.bilibili.Maodun.net.qiujuer2.lesson.sample.server;

import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.jdk.demo.nio.bilibili.Maodun.net.qiujuer2.lesson.sample.foo.Foo;
import com.jdk.demo.nio.bilibili.Maodun.net.qiujuer2.lesson.sample.foo.handle.ConnectorCloseChain;
import com.jdk.demo.nio.bilibili.Maodun.net.qiujuer2.lesson.sample.foo.handle.ConnectorStringPacketChain;
import com.jdk.demo.nio.bilibili.Maodun.net.qiujuer2.lesson.sample.server.handle.ClientHandler;
import com.jdk.demo.nio.bilibili.Maodun.net.qiujuer2.library.clink.box.StringReceivePacket;
import com.jdk.demo.nio.bilibili.Maodun.net.qiujuer2.library.clink.core.Connector;
import com.jdk.demo.nio.bilibili.Maodun.net.qiujuer2.library.clink.utils.CloseUtils;

public class TCPServer implements ServerAcceptor.AcceptListener, Group.GroupMessageAdapter {
    private final int port;
    private final File cachePath;
    private final ExecutorService deliveryPool;
    private final List<ClientHandler> clientHandlerList = new ArrayList<>();
    private final Map<String, Group> groups = new HashMap<>();
    private ServerAcceptor acceptor;
    private ServerSocketChannel server;

    private final ServerStatistics statistics = new ServerStatistics();

    public TCPServer(int port, File cachePath) {
        this.port = port;
        this.cachePath = cachePath;
        // 转发线程池
        this.deliveryPool = Executors.newSingleThreadExecutor();
        this.groups.put(Foo.DEFAULT_GROUP_NAME, new Group(Foo.DEFAULT_GROUP_NAME, this));
    }

    public boolean start() {
        try {
        	// 启动Acceptor线程
            ServerAcceptor acceptor = new ServerAcceptor(this);

            ServerSocketChannel server = ServerSocketChannel.open();
            // 设置为非阻塞
            server.configureBlocking(false);
            // 绑定本地端口
            server.socket().bind(new InetSocketAddress(port));
            // 注册客户端连接到达监听
            server.register(acceptor.getSelector(), SelectionKey.OP_ACCEPT);

            this.server = server;
            this.acceptor = acceptor;

            // 线程需要启动
            acceptor.start();
            if (acceptor.awaitRunning()) {
                System.out.println("服务器准备就绪～");
                System.out.println("服务器信息：" + server.getLocalAddress().toString());
                return true;
            } else {
                System.out.println("启动异常！");
                return false;
            }
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    public void stop() {
        if (acceptor != null) {
        	acceptor.exit();
        }

        synchronized (clientHandlerList) {
            for (ClientHandler clientHandler : clientHandlerList) {
                clientHandler.exit();
            }

            clientHandlerList.clear();
        }

        CloseUtils.close(server);

        // 停止线程池
        deliveryPool.shutdownNow();
    }

    void broadcast(String str) {
    	str = "系统通知: " + str;
    	synchronized(clientHandlerList) {
	    	for (ClientHandler clientHandler : clientHandlerList) {
	            sendMessageToClient(clientHandler, str);
	        }
    	}
    }

    /**
     * 发送消息给某个客户端
     *
     * @param handler 客户端
     * @param msg     消息
     */
    @Override
	public void sendMessageToClient(ClientHandler handler, String msg) {
        handler.send(msg);
        statistics.sendSize++;
    }

    /**
     * 获取当前的状态信息
     */
    Object[] getStatusString() {
        return new String[]{
                "客户端数量：" + clientHandlerList.size(),
                "发送数量：" + statistics.sendSize,
                "接收数量：" + statistics.receiveSize
        };
    }

    /**
     * 新客户端链接时回调
     *
     * @param channel 新客户端
     */
    @Override
    public void onNewSocketArrived(SocketChannel channel) {
        try {
        	ClientHandler clientHandler = new ClientHandler(channel, cachePath, deliveryPool);
            System.out.println(clientHandler.getClientInfo() + ":Connected!");

            // 添加收到消息的处理责任链
            clientHandler.getStringPacketChain()
                    .appendLast(statistics.statisticsChain())
                    .appendLast(new ParseCommandConnectorStringPacketChain());

            // 添加关闭链接时的责任链
            clientHandler.getCloseChain()
                    .appendLast(new RemoveQueueOnConnectorClosedChain());

            synchronized (TCPServer.this) {
            	clientHandlerList.add(clientHandler);
                System.out.println("当前客户端数量：" + clientHandlerList.size());
            }
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("客户端链接异常：" + e.getMessage());
        }
    }

    /**
     * 移除队列，在链接关闭回调时
     */
    private class RemoveQueueOnConnectorClosedChain extends ConnectorCloseChain {

        @Override
        protected boolean consume(ClientHandler handler, Connector connector) {
            synchronized (clientHandlerList) {
            	clientHandlerList.remove(handler);
            	// 移除群聊的客户端
            	Group group = groups.get(Foo.DEFAULT_GROUP_NAME);
            	group.removeMember(handler);
            }
            return true;
        }
    }

    /**
     * 解析收到的消息，当前节点主要做命令的解析，
     * 如果子节点也未进行数据消费，那么则进行二次消费，直接返回收到的数据
     */
    private class ParseCommandConnectorStringPacketChain extends ConnectorStringPacketChain {
        @Override
        protected boolean consume(ClientHandler handler, StringReceivePacket stringReceivePacket) {
            String str = stringReceivePacket.entity();
            if (str.startsWith(Foo.COMMAND_GROUP_JOIN)) {
                Group group = groups.get(Foo.DEFAULT_GROUP_NAME);
                if (group.addMember(handler)) {
                    sendMessageToClient(handler, "Join Group:" + group.getName());
                }
                return true;
            } else if (str.startsWith(Foo.COMMAND_GROUP_LEAVE)) {
                Group group = groups.get(Foo.DEFAULT_GROUP_NAME);
                if (group.removeMember(handler)) {
                    sendMessageToClient(handler, "Leave Group:" + group.getName());
                }
                return true;
            }
            return false;
        }

        @Override
        protected boolean consumeAgain(ClientHandler handler, StringReceivePacket stringReceivePacket) {
            // 捡漏的模式，当我们第一遍未消费，然后又没有加入到群，自然没有后续的节点消费
            // 此时我们进行二次消费，返回发送过来的消息
            sendMessageToClient(handler, stringReceivePacket.entity());
            return true;
        }
    }

}
